#ifndef MULTIGRID_AGGREGATION_AGGREGATION_HPP
#define MULTIGRID_AGGREGATION_AGGREGATION_HPP

/*! \file aggregation.hpp
 *  \author Zhao Chengpeng (chengpeng_zhao@foxmail.com)
 *  \date 2023-03-02
 *  \modified 2024-01-10
 *  \brief multigrid that stores R and P explicitly (csr format here)
 */
#include "PtrList.hpp"
#include "coarsening.hpp"
#include "csrMatrix.hpp"
#include "unapVector.hpp"

namespace UNAP {

template <typename Cmpt>
class Aggregation : public Coarsening<Cmpt> {
 protected:
  /// \brief finest matrix
  const CSRMatrix<Cmpt> *finestMatrix_;

  /// \brief hierarchy of  matrix levels
  PtrList<CSRMatrix<Cmpt> > coarseMatrixLevels_;

  PtrList<CSRMatrix<Cmpt> > P_;

  PtrList<CSRMatrix<Cmpt> > R_;

  /// Over-interpolation factor \f$\alpha\f$.
  scalar alpha_;

 public:
  Aggregation(const CSRMatrix<Cmpt> &A, label rowSizeInCoarsestLevel = 50);
  /// \brief return the matrix of giving level
  /// \param leveli index of giving level
  virtual const Matrix<Cmpt> &matrixLevel(const label leveli) const;
  virtual const Matrix<Cmpt> &coarseMatrix(const label leveli) const;
  const CSRMatrix<Cmpt> &Pmatrix(const label leveli) const;
  const CSRMatrix<Cmpt> &Rmatrix(const label leveli) const;

  ~Aggregation() {}

  void agglomerate();
  bool agglomerate(const CSRMatrix<Cmpt> &fineA, label level);

  void SET_scaleFactor(const label i) { alpha_ = i; }

  /// \brief set the maximum number of levles
  /// \param i maximum number of levles
  virtual void SET_maxLevels(const label i);
  /// \brief shrink the number of levels to that specified
  /// \param nCreatedLevels number of resulting levels specified
  virtual void compactLevels(const label nCreatedLevels);

  IMPL_RENDER()

 private:
  /// \brief restrict (integrate by summation) cell field
  /// \tparam T vector type
  /// \param cf coarse level vector
  /// \param ff fine level vector
  /// \param fineLevelIndex fine level index
  template <typename T>
  void restrictField_internal(Vector<T> &cf, const Vector<T> &ff,
                              const label fineLevelIndex) const {
    const Matrix<Cmpt> *R = &Rmatrix(fineLevelIndex);
    R->spMV(cf, ff);
  }

  /// \brief prolong (interpolate by injection) cell field
  /// \tparam T vector type
  /// \param cf coarse level vector
  /// \param ff fine level vector
  /// \param coarseLevelIndex coarse level index
  template <typename T>
  void prolongField_internal(Vector<T> &ff, const Vector<T> &cf,
                             const label coarseLevelIndex) const {
    const Matrix<Cmpt> *P = &Pmatrix(coarseLevelIndex);
    P->spMV(ff, cf);
  }
};

} /* End namespace UNAP */
#endif /* end of include guard: MULTIGRID_AGGREGATION_AGGREGATION_HPP */
